using System;
using System.Collections.Generic;
using System.IO;
using System.Threading;
using System.Drawing;
using System.Drawing.Imaging;
using System.Text;
using System.Diagnostics;
using AcedUtils;

namespace BrainTechLLC.Communications
{
  /// <summary>
  /// Stores a rectangle of pixels (used to send updates for regions of the screen that have changed)
  /// </summary>
  public class GraphicsArea
  {
    private AcedUtils.AcedCompressionLevel compressionLevel = AcedCompressionLevel.Normal;

    public GraphicsArea(AcedCompressionLevel compression)
    {
      compressionLevel = compression;
      contentType = ByteBufferContentType.UncompressedDifferenceMap;
    }

    protected GraphicsArea(AcedCompressionLevel compression, Rectangle rect, byte[] bytes, ByteBufferContentType bufferContentType, int nDataLen)
    {
      compressionLevel = compression;
      Rectangle = rect;
      byteData = bytes;
      nDataLength = nDataLen;
      contentType = bufferContentType;
    }

    public int nDataLength;
    public int Left;
    public int Top;
    public int Width;
    public int Height;

    /// <summary>
    /// Stores the byte data associated with this region
    /// </summary>
    public byte[] byteData;

    public Rectangle Rectangle
    {
      get { return new Rectangle(Left, Top, Width, Height); }
      set
      {
        Left = value.Left;
        Top = value.Top;
        Width = value.Width;
        Height = value.Height;
      }
    }

    private ByteBufferContentType contentType;

    /// <summary>
    /// Sets (and possibly changes) the byte buffer content type.  This method is used
    /// to compress and decompress difference maps (i.e. regions of the screen that have changed)
    /// </summary>
    /// <param name="value"></param>
    public void SetByteContentType(ByteBufferContentType value)
    {
      if (contentType == ByteBufferContentType.UncompressedDifferenceMap)
      {
        switch (value)
        {
          case ByteBufferContentType.UncompressedDifferenceMap:
            // We're already uncompressed.  No conversion necessary
            break;
          case ByteBufferContentType.CompressedDifferenceMap:
            byte[] rgbTemp = new byte[nDataLength + 4];
            Monitor.Enter(AcedUtils.AcedDeflator.Instance);
            int nNewLen = 0;
            try
            {
              nNewLen = AcedDeflator.Instance.Compress(
                byteData, 0,
                nDataLength,
                compressionLevel,
                rgbTemp, 0
                );
            }
            finally { Monitor.Exit(AcedUtils.AcedDeflator.Instance); }
            byteData = null;
            byteData = new byte[nNewLen + 16];
            Array.Copy(rgbTemp, byteData, nNewLen);
            nDataLength = nNewLen;
            contentType = value;
            break;
          default:
            Debug.Assert(false, "Conversion from " + contentType.ToString() + " to " + value.ToString() + " is not supported.");
            break;
        }
      }
      else if (contentType == ByteBufferContentType.CompressedDifferenceMap)
      {
        switch (value)
        {
          case ByteBufferContentType.CompressedDifferenceMap:
            // we're already compressed.  No conversion necessary
            break;
          case ByteBufferContentType.UncompressedDifferenceMap:
            byte[] rgDecompressed = null;
            int nDecompressedLength = 0;
            Monitor.Enter(AcedUtils.AcedInflator.Instance);
            try
            {
              nDecompressedLength = AcedInflator.GetDecompressedLength(byteData, 0);
              rgDecompressed = AcedInflator.Instance.Decompress(byteData, 0, 0, 0);
            }
            finally { Monitor.Exit(AcedUtils.AcedInflator.Instance); }
            if (rgDecompressed != null)
            {
              byteData = rgDecompressed;
              nDataLength = nDecompressedLength;
            }
            contentType = value;
            break;
          default:
            Debug.Assert(false, "Conversion from " + contentType.ToString() + " to " + value.ToString() + " is not supported.");
            break;
        }
      }
      else
      {
        Debug.Assert(false, "Conversion from " + contentType.ToString() + " is not supported.");
      }
    }

    public ByteBufferContentType ContentType { get { return contentType; } }
  }

  /// <summary>
  /// ScreenUpdateData stores information about a screen region that has changed
  /// </summary>
  public class ScreenUpdateData : GraphicsArea
  {
    public ScreenUpdateData(AcedCompressionLevel comp, Rectangle rect, byte[] byteData, ByteBufferContentType bufferContentType, int nDataLen)
      : base(comp, rect, byteData, bufferContentType, nDataLen) { }
    public ScreenUpdateData(Rectangle rect, byte[] byteData, ByteBufferContentType bufferContentType, int nDataLen)
      : base(AcedCompressionLevel.Normal, rect, byteData, bufferContentType, nDataLen) { }
  }

  /// <summary>
  /// A screen update data packet contains a full sequential list of screen updates that occurred on
  /// the remote machine since the last screen update
  /// </summary>
  public class ScreenUpdateDataPacket
  {
    public ScreenUpdateDataPacket(List<ScreenUpdateData> listOfScreenUpdates) { rgScreenUpdateData = listOfScreenUpdates; }
    public List<ScreenUpdateData> rgScreenUpdateData;
  }

}
